/*
                          qpdf

    Copyright (C) 2015 Arthur Benilov,
    arthur.benilov@gmail.com
    This program is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.
    This software is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
    Lesser General Public License for more details.
*/

#include <QDebug>
#include <QFile>
#include <QFileInfo>
#include <QVBoxLayout>
#include "PdfJSBridge.h"
#include "QPdfWidget.h"

// NOTE:
// There is this bug in Qt https://bugreports.qt.io/browse/QTBUG-46973
// which causes the application to crash when loading from qrc:/ resources.
// Reported to be fixed as from Qt 5.6.0

const QString cPdfViewerUrl("qrc:/pdfview/viewer.html");

// class private members
struct QPdfWidgetPrivate
{
    PdfJsBridge* pPdfJsBridge = nullptr;
    bool ready;
    QByteArray pdfData;
    QString pdfFile;
    //HelpReaderPopUp* pPopUp = nullptr;
};

QPdfWidget::QPdfWidget(QWidget* pParent)
    : QWidget(pParent)
    , m_nPageCount(0)
{
    m_widgetPrivate = new QPdfWidgetPrivate;
    m_widgetPrivate->pPdfJsBridge = new PdfJsBridge();
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::initialized, this, &QPdfWidget::Initialized);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::loadFinished, this, &QPdfWidget::OnLoadFinished);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::pdfDocumentloaded, this, &QPdfWidget::OnRenderPdfFinished);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::pageNumberChanged, this, &QPdfWidget::OnPageNumberChanged);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::pageCountChanged, this, &QPdfWidget::OnPageCountChanged);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::pageScaleChanged, this, &QPdfWidget::OnPageScaleChanged);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::chapterListLoaded, this, &QPdfWidget::OnChapterListLoaded);
    connect(m_widgetPrivate->pPdfJsBridge, &PdfJsBridge::findStateChanged, this, &QPdfWidget::OnFindStateChanged);
    m_widgetPrivate->ready = false;

    // Initialize pdf.js viewer
    QUrl url = QUrl::fromUserInput(cPdfViewerUrl);
    m_widgetPrivate->pPdfJsBridge->setUrl(url);

    // widget layout
    QVBoxLayout* pLayout = new QVBoxLayout();
    pLayout->setMargin(0);
    pLayout->addWidget(m_widgetPrivate->pPdfJsBridge);
    setLayout(pLayout);
}

QPdfWidget::~QPdfWidget()
{
    delete m_widgetPrivate;
}

bool QPdfWidget::LoadFile(const QString& path)
{
    QFileInfo fi(path);
    if (fi.exists())
    {
        m_widgetPrivate->pdfFile = path;
#ifdef QPDF_WIDGET_USE_CORS
        renderPdfFile(path);
#else
        QFile file(path);
        if (file.open(QIODevice::ReadOnly))
        {
            QByteArray data = file.readAll();
            LoadData(data);
        }
        else
        {
            return false;
        }
#endif
        return true;
    }

    return false;
}

void QPdfWidget::LoadData(const QByteArray& data)
{
    m_widgetPrivate->pdfData = data;
    RenderPdfData();
}

void QPdfWidget::CloseDocument()
{
    m_widgetPrivate->pPdfJsBridge->close();
}

void QPdfWidget::SetPage(int nPage)
{
    if (nPage > m_nPageCount || nPage < 0)
    {
        ShowPopUpWindow(0);
        return;
    }
    QString script = QString("PDFViewerApplication.page = %1").arg(nPage);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

int QPdfWidget::Page() const
{
    QVariant res = m_widgetPrivate->pPdfJsBridge->invokeJavaScriptAndWaitForResult("PDFViewerApplication.page");
    if (res.isValid())
    {
        return res.toInt();
    }
    return 0;
}

int QPdfWidget::PagesCount() const
{
    QVariant res = m_widgetPrivate->pPdfJsBridge->invokeJavaScriptAndWaitForResult("PDFViewerApplication.pagesCount");
    if (res.isValid())
    {
        return res.toInt();
    }
    return 0;
}

void QPdfWidget::RotatePages(int degrees)
{
    QString script = QString("PDFViewerApplication.rotatePages(%1)").arg(degrees);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::ZoomIn(int ticks)
{
    QString script = QString("PDFViewerApplication.zoomIn(%1)").arg(ticks);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::ZoomOut(int ticks)
{
    QString script = QString("PDFViewerApplication.zoomOut(%1)").arg(ticks);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::ZoomReset(qreal scale)
{
    QString script = QString("PDFViewerApplication.pdfViewer.currentScaleValue=%1").arg(scale);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::ShowDocumentProperties()
{
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript("PDFViewerApplication.pdfDocumentProperties.open()");
}

void QPdfWidget::SetToolbarVisible(bool v)
{
    m_widgetPrivate->pPdfJsBridge->setToolbarVisible(v);
}

void QPdfWidget::SetFindBarVisible(bool v)
{
    QString script = QString("PDFViewerApplication.findBar.%1()").arg(v ? "open" : "close");
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::FindTextInDocument(const QString& text)
{
    SetFindFieldText(text);
    // 	if (!text.isEmpty()) {
    // 		SetFindFieldText(text);
    // 	}

    // Sending an empty event will actually send 'find' since it will be prepended to
    // the event name.
    // All options options are (viewer.js:1155):
    // 'find',
    // 'findagain',
    // 'findhighlightallchange',
    // 'findcasesensitivitychange'

    m_widgetPrivate->pPdfJsBridge->invokeJavaScript("PDFViewerApplication.findBar.dispatchEvent('', false);");
}

void QPdfWidget::FindNext()
{
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript("PDFViewerApplication.findBar.dispatchEvent('again', false);");
}

void QPdfWidget::FindPrevious()
{
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript("PDFViewerApplication.findBar.dispatchEvent('again', true);");
}

int QPdfWidget::FindResultsCount() const
{
    QVariant res = m_widgetPrivate->pPdfJsBridge->invokeJavaScriptAndWaitForResult(
        "PDFViewerApplication.findController.matchCount");
    if (res.isValid())
    {
        return res.toInt();
    }
    return 0;
}

void QPdfWidget::NavigateTo(const QString& dest)
{
    QString escapedDest = dest;
    escapedDest.replace("\"", "\\\"");
    QString script = QString("PDFViewerApplication.pdfLinkService.navigateTo(\"%1\")").arg(escapedDest);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

QStringList QPdfWidget::GetDestinations() const
{
    return m_widgetPrivate->pPdfJsBridge->fetchPdfDocumentDestinations();
}

QWebEngineView* QPdfWidget::InternalWebEngineView() const
{
    return dynamic_cast<QWebEngineView*>(m_widgetPrivate->pPdfJsBridge);
}

QList<QJsonObject> QPdfWidget::GetChapterList() const
{
    return m_widgetPrivate->pPdfJsBridge->getChapterList();
}

void QPdfWidget::GotoChapter(const QString& strHref) const
{
    m_widgetPrivate->pPdfJsBridge->gotoChapter(strHref);
}

int QPdfWidget::GetCurrentPage() const
{
    int nCurrentPage = m_widgetPrivate->pPdfJsBridge->GetCurrentPage();
    return nCurrentPage;
}

void QPdfWidget::SetLanguage(const QString & strLanguage)
{
    m_strLanguage = strLanguage;
}

void QPdfWidget::OnLoadFinished(bool status)
{
    if (!status)
    {
        qCritical() << "Unable to initialize the web view";
        return;
    }

    m_widgetPrivate->ready = true;
    if (!m_widgetPrivate->pdfData.isEmpty())
    {
        RenderPdfData();
    }
    else if (!m_widgetPrivate->pdfFile.isEmpty())
    {
        RenderPdfFile(m_widgetPrivate->pdfFile);
    }
    emit LoadFinished();
}

void QPdfWidget::RenderPdfData()
{
    if (!m_widgetPrivate->ready)
    {
        return;
    }

    m_widgetPrivate->pdfData = m_widgetPrivate->pdfData.toBase64();
    QString script = QString("qpdf_ShowPdfFileBase64('%1')").arg(QString::fromUtf8(m_widgetPrivate->pdfData));

    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);

    // Clear pdf data
    m_widgetPrivate->pdfData.clear();
}

void QPdfWidget::RenderPdfFile(const QString& file)
{
    if (!m_widgetPrivate->ready)
    {
        return;
    }

    m_widgetPrivate->pdfData = m_widgetPrivate->pdfData.toBase64();
    QString script = QString("qpdf_ShowPdfFile('file://%1')").arg(file);

    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);

    // Clear pdf data
    m_widgetPrivate->pdfData.clear();
}

void QPdfWidget::OnRenderPdfFinished()
{
    emit PdfDocumentLoaded();
}

void QPdfWidget::OnPageNumberChanged(int nPageNumber)
{
    emit pageNumberChanged(nPageNumber);
}

void QPdfWidget::OnPageCountChanged(int nPageCount)
{
    m_nPageCount = nPageCount;
    emit pageCountChanged(nPageCount);
}

void QPdfWidget::OnPageScaleChanged(double fScale)
{
    emit pageScaleChanged(fScale);
}

void QPdfWidget::OnPopUpWindowClosed()
{
}

void QPdfWidget::OnFindStateChanged(int nState)
{
    ShowPopUpWindow(nState + 1);
}

void QPdfWidget::OnChapterListLoaded(QList<QJsonObject>& list)
{
    emit chapterListLoaded(list);
}

void QPdfWidget::SetFindFieldText(const QString& text)
{
    // jinjunjie1 2023-6-1 注释掉，否则查找不了，原因不明
    //  Reset find controller.
    // m->pPdfJsBridge->invokeJavaScriptAndWaitForResult("PDFViewerApplication.findController.reset()");

    // Fill-in the find field.
    QString escapedText = text;
    escapedText.replace("\"", "\\\"");
    QString script = QString("PDFViewerApplication.findBar.findField.value=\"%1\"").arg(escapedText);
    m_widgetPrivate->pPdfJsBridge->invokeJavaScript(script);
}

void QPdfWidget::ShowPopUpWindow(int nTipType)
{
}
